#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Task.hpp"

using namespace std;

#define PROCESS_NUM 5

// 如果父进程没有给子进程派发任务,子进程就阻塞等待任务
int waitCommand(int waitFd, bool& quit)
{
    uint32_t command = 0;
    ssize_t s = read(waitFd, &command, sizeof(command));
    // 写端退出,读端读到0,则写端也要退出
    if(s == 0)
    {
        quit = true;
        return -1;
    }

    assert(s == sizeof(uint32_t));  // 要求必须读到4给字节
    return command;
}

void sendAndWakeup(pid_t who, int fd, uint32_t command)
{
    write(fd, &command, sizeof(command));
    // 父进程通过fd唤醒子进程并给它派发任务desc[command]
    cout << "father process call child process[" << who << "] execul " << desc[command] << " through " << fd << endl;
}

int main()
{
    // 加载任务
    load();
    // pid_t是子进程的id, int是写端的fd
    vector<pair<pid_t, int>> slots;
    // 先创建多个进程
    for(int i = 0; i < PROCESS_NUM; ++i)
    {
        // 创建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        assert(n == 0); // 判断管道是否创建成功
        (void)n;

        // 创建子进程
        pid_t id = fork();
        assert(id != -1);   // 判断子进程是否创建成功
        // child process
        if(id == 0)
        {
            // 关闭子进程的写端
            close(pipefd[1]);
            // 子进程等待父进程派发任务
            while(true)
            {
                // false表示父进程的写端没有关闭
                bool quit = false;
                // 如果父进程不派发任务,子进程就阻塞等待
                int command = waitCommand(pipefd[0], quit);
                // 父进程的写端关闭,子进程的读端也要退出
                if(quit)
                    break;

                // 执行对应的任务
                if(command >= 0 && command < handlerSize())
                    callBacks[command]();
                else
                    cout << "非法command: " << command << endl;
            }
            cout << "sender quit, receiver quit too!!!" << endl;  
            close(pipefd[0]);
            exit(0);
        }

        // father process
        // 关闭父进程的读端,将子进程的id和父进程的写端保存到slots中
        close(pipefd[0]);
        slots.push_back(make_pair(id, pipefd[1]));
    }

    // 父进程随机给子进程派发任务
    srand((unsigned int)(time(nullptr) ^ getpid() ^ 2023222));    // 让数据源更随机
    int count = 0;  // 父进程给子进程总共派发5个任务后,关闭父进程的所有写端
    while(true)
    {
        // 随机选取一个任务
        int command = rand() % handlerSize();
        // 随机选取一个子进程,随机数方式的负载均衡
        int choice = rand() % slots.size();
        // 把任务派发给指定的进程
        sendAndWakeup(slots[command].first, slots[command].second, command);
        sleep(1);

        ++count;
        if(count == 5)
        {
            cout << "父进程的任务全部派发完成" << endl;
            break;
        }

        // 下方的代码是用户指定做哪一个任务
        // int select;
        // int command;
        // cout << endl;
        // cout << "############################################" << endl;
        // cout << "#   1. show funcitons      2.send command  #" << endl;
        // cout << "############################################" << endl;
        // cout << "Please Select> ";
        // cin >> select;
        // if (select == 1)
        //     showHandler();
        // else if (select == 2)
        // {
        //     cout << "Enter Your Command> ";
        //     // 选择任务
        //     cin >> command;
        //     // 选择进程
        //     int choice = rand() % slots.size();
        //     // 把任务给指定的进程
        //     sendAndWakeup(slots[choice].first, slots[choice].second, command);
        //     sleep(1);
        // }
        // else
        // {
        //     cout << "select error!" << endl;
        //     continue;
        // }
    }

    // 关闭fd, 所有的子进程都会读到0,关闭读端并退出
    for (const auto &slot : slots)
    {
        close(slot.second);
    }

    // 等待子进程
    for (const auto &slot : slots)
    {
        waitpid(slot.first, nullptr, 0);
    }

    return 0;
}
